package Metadata;

import java.io.IOException;
import java.util.ArrayList;

import Transaction.Transaction;
import Common.GlobalConst;
import Common.GoggleDB;
import Common.IndexType;
import Common.Types;
import heap.Heapfile;
import heap.RID;
import heap.Scan;
import heap.Tuple;
import exceptions.*;

public class TableMgr extends Heapfile implements GlobalConst {

	public TableMgr(String tablename, Transaction tx) throws Exception {
		super(tablename, tx);
		tuple = new Tuple(Tuple.maxSize);

		attrs = new Types[6];
		// table name
		attrs[0] = new Types(Types.CHAR);
		// attribute count
		attrs[1] = new Types(Types.INT);
		// index count
		attrs[2] = new Types(Types.INT);
		// number of tuples
		attrs[3] = new Types(Types.INT);
		// number of page
		attrs[4] = new Types(Types.INT);
		// primary key
		attrs[5] = new Types(Types.INT);

		strSizes = new short[6];
		strSizes[0] = (short) MAXNAME;

		// tuple.setHdr((short) 6, attrs, str_sizes);
		creatTuple();
	}

	private void creatTuple() throws Exception, InvalidTypeException,
			IOException {
		tuple = new Tuple(Tuple.maxSize);
		initTuple();
	}

	private void initTuple() throws Exception, InvalidTypeException,
			IOException {
		tuple.setHdr((short) 6, attrs, strSizes);

	}

	public boolean getInfo(String tableName, tableSchema record, Transaction tx)
			throws Exception {
		RID rid = null;
		Scan pscan = null;
		pscan = new Scan(this, tx);
		rid = new RID();

		// System.out.println("dfj:"+tableName);
		while (true) {

			tuple = pscan.getNext(rid, tx, fileName);

			if (tuple == null) {
				// System.out.println("dfj22:"+tableName);
				return false;
			}
			initTuple();
			// System.out.println("tuple:"+tuple);
			readTuple(tuple, record, tx);

			if (record.relName.equalsIgnoreCase(tableName) == true) {
				// System.out.println("tablename:"+record.relName);
				pscan.closeScan();
				return true;
			}
		}
	}

	// CREATE A NEW RELATION
	public void createRel(String relation, int attrCnt, attrInfo[] attrList,
			Transaction tx) throws Exception {
		Heapfile rel;
		tableSchema rd = null;
		attrSchema ad = null;
		attrSchema[] attr = null;
		int tupleWidth = 0;
		int offset = 0;
		int sizeOfInt = 4;
		int sizeOfFloat = 4;
		int j;
		boolean status = true;

		rd = new tableSchema();
		status = getInfo(relation, rd, tx);

		if (status == true) {
			throw new DBCatalogrelExistsException(null, "Relation Exists!");
		}
		// MAKE SURE THERE ARE NO DUPLICATE ATTRIBUTE NAMES

		attr = new attrSchema[attrCnt];
		for (int i = 0; i < attrCnt; i++) {
			int type = attrList[i].attrType.attrType;
			if ( type== Types.CHAR||type==Types.DATETIME||type==Types.DECIMAL||type==Types.VARCHAR  ||type==Types.TIMESTAMP)
				j = 0;
			// tupleWidth += sizeof(String);
			else if (attrList[i].attrType.attrType == Types.INT || type==Types.BOOLEAN)
				tupleWidth += sizeOfInt;
			else if (attrList[i].attrType.attrType == Types.FLOAT)
				tupleWidth += sizeOfFloat;
			/* Duplicate attributes. */
			/*
			 * for (j = 0; j < i; j++) if
			 * (attrList[i].attrName.equalsIgnoreCase(attrList[j].attrName) ==
			 * true) throw new Catalogdupattrs(null, "Duplicate Attributes!");
			 */
		}

		rd = new tableSchema();
		rd.relName = new String(relation);
		rd.attrCnt = attrCnt;
		rd.indexCnt = 0;
		rd.numTuples = NUMTUPLESFILE;
		rd.numPages = NUMPAGESFILE;

		addInfo(rd, tx);
		ad = new attrSchema();
		ad.relName = new String(relation);
		
		for (int i = 0; i < attrCnt; i++) {
			ad.attrName = new String(attrList[i].attrName);
			ad.attrOffset = offset;
			ad.attrType = new Types(attrList[i].attrType.attrType);
			ad.indexCnt = 0;
			ad.attrPos = i + 1; // field position in the record
			ad.pk = attrList[i].pk;
			int type = attrList[i].attrType.attrType;
			if (attrList[i].attrType.attrType == Types.CHAR||type==Types.DATETIME||type==Types.DECIMAL||type==Types.VARCHAR ||type==Types.TIMESTAMP) {
				ad.attrLen = attrList[i].attrLen;
				// System.out.println("attrlen:"+i+" "+attrList[i].attrLen+"
				// "+attrList[i].attrName);
				ad.maxVal = new attrData();
				ad.minVal = new attrData();
				ad.maxVal.strVal = new String("Z");
				ad.minVal.strVal = new String("A");
			} else if (attrList[i].attrType.attrType == Types.INT|| type==Types.BOOLEAN) {
				ad.attrLen = 4;
				ad.maxVal = new attrData(); 
				ad.minVal = new attrData();
				ad.minVal.intVal = MINNUMVAL;
				ad.maxVal.intVal = MAXNUMVAL;
			} else if (attrList[i].attrType.attrType == Types.FLOAT) {
				ad.attrLen = 4;
				ad.maxVal = new attrData();
				ad.minVal = new attrData();
				ad.minVal.floatVal = MINNUMVAL;
				ad.maxVal.floatVal = MAXNUMVAL;
			}

			attr[i]=ad;
			GoggleDB.attrMgr.addInfo(ad, tx);
			offset += ad.attrLen;
		}
		//relInfo.put(relation, attr);
		//tableInfo.put(relation, rd);
		// NOW CREATE HEAPFILE
		rel = new Heapfile(relation, tx);

	}

	
	public void CreatExitTable (String oldtable,String relation,Transaction tx)throws Exception {
		Heapfile rel;
		tableSchema rd = null;
		attrSchema ad = null;
		attrSchema[] attr = null;
		int tupleWidth = 0;
		int offset = 0;
		int sizeOfInt = 4;
		int sizeOfFloat = 4;
		int j;
		boolean status = true;

		rd = new tableSchema();
		status = getInfo(relation, rd, tx);

		if (status == true) {
			throw new DBCatalogrelExistsException(null, "Relation Exists!");
		}
		// MAKE SURE THERE ARE NO DUPLICATE ATTRIBUTE NAMES

		attrSchema[] attrList = GoggleDB.attrMgr.getTableInfo(oldtable, tx);
		int attrCnt = attrList.length;
		attr = new attrSchema[attrCnt];
		for (int i = 0; i < attrCnt; i++) {
			int type = attrList[i].attrType.attrType;
			if ( type== Types.CHAR||type==Types.DATETIME||type==Types.DECIMAL||type==Types.VARCHAR  ||type==Types.TIMESTAMP)
				j = 0;
			// tupleWidth += sizeof(String);
			else if (attrList[i].attrType.attrType == Types.INT || type==Types.BOOLEAN)
				tupleWidth += sizeOfInt;
			else if (attrList[i].attrType.attrType == Types.FLOAT)
				tupleWidth += sizeOfFloat;
			/* Duplicate attributes. */
			/*
			 * for (j = 0; j < i; j++) if
			 * (attrList[i].attrName.equalsIgnoreCase(attrList[j].attrName) ==
			 * true) throw new Catalogdupattrs(null, "Duplicate Attributes!");
			 */
		}

		rd = new tableSchema();
		rd.relName = new String(relation);
		rd.attrCnt = attrCnt;
		rd.indexCnt = 0;
		rd.numTuples = NUMTUPLESFILE;
		rd.numPages = NUMPAGESFILE;

		addInfo(rd, tx);
		ad = new attrSchema();
		ad.relName = new String(relation);
		
		for (int i = 0; i < attrCnt; i++) {
			ad.attrName = new String(attrList[i].attrName);
			ad.attrOffset = offset;
			ad.attrType = new Types(attrList[i].attrType.attrType);
			ad.indexCnt = 0;
			ad.attrPos = i + 1; // field position in the record
			ad.pk = attrList[i].pk;
			int type = attrList[i].attrType.attrType;
			if (attrList[i].attrType.attrType == Types.CHAR||type==Types.DATETIME||type==Types.DECIMAL||type==Types.VARCHAR ||type==Types.TIMESTAMP) {
				ad.attrLen = attrList[i].attrLen;
				// System.out.println("attrlen:"+i+" "+attrList[i].attrLen+"
				// "+attrList[i].attrName);
				ad.maxVal = new attrData();
				ad.minVal = new attrData();
				ad.maxVal.strVal = new String("Z");
				ad.minVal.strVal = new String("A");
			} else if (attrList[i].attrType.attrType == Types.INT|| type==Types.BOOLEAN) {
				ad.attrLen = 4;
				ad.maxVal = new attrData();
				ad.minVal = new attrData();
				ad.minVal.intVal = MINNUMVAL;
				ad.maxVal.intVal = MAXNUMVAL;
			} else if (attrList[i].attrType.attrType == Types.FLOAT) {
				ad.attrLen = 4;
				ad.maxVal = new attrData();
				ad.minVal = new attrData();
				ad.minVal.floatVal = MINNUMVAL;
				ad.maxVal.floatVal = MAXNUMVAL;
			}

			attr[i]=ad;
			GoggleDB.attrMgr.addInfo(ad, tx);
			offset += ad.attrLen;
		}
		//relInfo.put(relation, attr);
		//tableInfo.put(relation, rd);
		// NOW CREATE HEAPFILE
		rel = new Heapfile(relation, tx);
		
		Heapfile oldHeap = new Heapfile(oldtable,tx);
		Scan pscan = new Scan(oldHeap,tx);
		RID rid = new RID();
		while(true) {
			tuple = pscan.getNext(rid, tx, oldtable);

			if (tuple == null) {
				// System.out.println("dfj22:"+tableName);
				break;
			}
			rel.insertRecord(tuple.getTupleByteArray(tx), tx, relation);
		}
	}
	
	public String getPrimaryKey(String relation, Transaction tx)
			throws Exception {
		tableSchema rd = new tableSchema();

		getInfo(relation, rd, tx);

		if (rd.primaryKey == 0) {
		}

		attrSchema attrs_pr[];

		attrs_pr = GoggleDB.attrMgr.getTableInfo(relation, tx);

		return attrs_pr[rd.primaryKey - 1].attrName;
	}

	// ADD AN INDEX TO A RELATION
	public void addIndex(String relation, String attrName,String indexName,
			IndexType accessType, Transaction tx) throws Exception {
		tableSchema rd = null;

		rd = new tableSchema();

		// GET RELATION DATA

		getInfo(relation, rd, tx);

		// CREATE INDEX FILE
		GoggleDB.indexMgr.addIndex(relation, attrName,indexName, accessType, tx);

		// MODIFY INDEXCNT IN RELCAT
		rd.indexCnt++;

		removeInfo(relation, tx);
		addInfo(rd, tx);
	}

	public void setPrimaryKey(String relation, String attrName, Transaction tx)
			throws Exception {
		tableSchema rd = new tableSchema();
		attrSchema ad = new attrSchema();

		getInfo(relation, rd, tx);

		ad = GoggleDB.attrMgr.getInfo(relation, attrName, tx);

		rd.primaryKey = ad.attrPos;
		removeInfo(relation, tx);
		addInfo(rd, tx);
	}

	public void destroyRel(String tableName, Transaction tx) throws Exception {

		tableSchema record = new tableSchema();

		getInfo(tableName, record, tx);

		IndexSchema[] indices = null;
		int cantIndices =0;
		indices = GoggleDB.indexMgr.getRelInfo(tableName, 0, tx);
		if(indices!=null)cantIndices = indices.length;

		for (int i = 0; i < cantIndices; ++i) {
			dropIndex(tableName,indices[i].IndexName, indices[i].accessType, tx);

		}

		attrSchema[] attributes = new attrSchema[record.attrCnt];

		attributes = GoggleDB.attrMgr.getTableInfo(record.relName, tx);

		for (int i = 0; i < record.attrCnt; ++i) {

			GoggleDB.attrMgr.removeInfo(tableName, attributes[i].attrName, tx);

		}

		// borro el Heapfile
		// 1) lo abro
		Heapfile hf;

		hf = new Heapfile(tableName, tx);

		hf.deleteFile(tx, tableName);

		removeInfo(tableName, tx);
	}

	public void dropIndex(String relation,String indexName,
			IndexType accessType, Transaction tx) throws Exception {

		GoggleDB.tableMgr.dropIndex(relation,indexName, accessType, tx);

		tableSchema relRec = null;
		relRec = new tableSchema();

		GoggleDB.tableMgr.getInfo(relation, relRec, tx);

		relRec.indexCnt--;
		removeInfo(relation, tx);
		addInfo(relRec, tx);

	}

	// ADD INFORMATION ON A RELATION TO CATALOG
	public void addInfo(tableSchema record, Transaction tx) throws Exception {
		creatTuple();
		makeTuple(tuple, record);
		insertRecord(tuple.getTupleByteArray(tx), tx, fileName);
	}

	// REMOVE INFORMATION ON A RELATION FROM CATALOG
	public void removeInfo(String tableName, Transaction tx) throws Exception {
		RID rid = null;
		Scan pscan = null;
		tableSchema record = null;
		pscan = new Scan(this, tx);

		rid = new RID();
		record = new tableSchema();

		while (true) {

			tuple = pscan.getNext(rid, tx, fileName);
			if (tuple == null) {

				pscan.closeScan();
				throw new DBCatalogNotFoundException(null,
						"Catalog table not Found!");
			}
			initTuple();
			readTuple(tuple, record, tx);

			if (record.relName.equalsIgnoreCase(tableName) == true) {
				pscan.closeScan();
				deleteRecord(rid, tx, fileName);
				return;
			}
		}
	}

	// Converts AttrDesc to tuple.
	public void makeTuple(Tuple tuple, tableSchema record) throws Exception {
		tuple.SsetStrFld(1, record.relName);
		tuple.SsetIntFld(2, record.attrCnt);
		tuple.SsetIntFld(3, record.indexCnt);
		tuple.SsetIntFld(4, record.numTuples);
		tuple.SsetIntFld(5, record.numPages);
		tuple.SsetIntFld(6, record.primaryKey);
	}

	public void readTuple(Tuple tuple, tableSchema record, Transaction tx)
			throws Exception {
		// System.out.println(tuple.getStrFld(1));
		record.relName = tuple.getStrFld(1, tx, fileName);
		record.attrCnt = tuple.getIntFld(2, tx, fileName);
		record.indexCnt = tuple.getIntFld(3, tx, fileName);
		record.numTuples = tuple.getIntFld(4, tx, fileName);
		record.numPages = tuple.getIntFld(5, tx, fileName);
		record.primaryKey = tuple.getIntFld(6, tx, fileName);

	}

	Tuple tuple;

	short[] strSizes;

	Types[] attrs;

	public void destroyTemoRel(Transaction tx) throws Exception {
		tableSchema record = new tableSchema();
		ArrayList<String> tableNames=GoggleDB.storageInterface.findAllTemp();
		for (String tableName:tableNames)
		{
		getInfo(tableName, record, tx);

		IndexSchema[] indices = null;
		int cantIndices =0;
		indices = GoggleDB.indexMgr.getRelInfo(tableName, 0, tx);
		if(indices!=null)cantIndices = indices.length;

		for (int i = 0; i < cantIndices; ++i) {
			dropIndex(tableName,indices[i].IndexName, indices[i].accessType, tx);

		}

		attrSchema[] attributes = new attrSchema[record.attrCnt];

		attributes = GoggleDB.attrMgr.getTableInfo(record.relName, tx);

		for (int i = 0; i < record.attrCnt; ++i) {

			GoggleDB.attrMgr.removeInfo(tableName, attributes[i].attrName, tx);

		}

		// borro el Heapfile
		// 1) lo abro
		Heapfile hf;

		hf = new Heapfile(tableName, tx);

		hf.deleteFile(tx, tableName);

		removeInfo(tableName, tx);
		
	}
	}

	//public HashMap<String,attrSchema[]> relInfo = new HashMap<String ,attrSchema[]>();
	//public HashMap<String ,tableSchema> tableInfo = new HashMap<String,tableSchema>();
}
